package ru.paswd.marketprice.ui.activity

import android.Manifest
import android.animation.ValueAnimator
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.yandex.mapkit.Animation
import com.yandex.mapkit.geometry.Point
import com.yandex.mapkit.map.CameraPosition
import com.yandex.mapkit.map.MapObject
import com.yandex.mapkit.map.MapObjectCollection
import com.yandex.mapkit.map.MapObjectTapListener
import com.yandex.runtime.image.ImageProvider
import kotlinx.android.synthetic.main.activity_price_map.*
import kotlinx.android.synthetic.main.bottom_sheet_price_map.*
import moxy.presenter.InjectPresenter
import ru.paswd.marketprice.R
import ru.paswd.marketprice.data.models.LatLng
import ru.paswd.marketprice.data.models.Price
import ru.paswd.marketprice.data.models.PriceWithShop
import ru.paswd.marketprice.data.models.Product
import ru.paswd.marketprice.helpers.YandexMapHelper
import ru.paswd.marketprice.ui.components.PricePointColor
import ru.paswd.marketprice.ui.presenter.PriceMapPresenter
import ru.paswd.marketprice.ui.view.PriceMapView
import ru.paswd.marketprice.ui.widgets.chart.PriceHistoryItem
import ru.paswd.marketprice.utils.DrawableUtils
import ru.paswd.marketprice.utils.NumericUtils
import ru.paswd.marketprice.utils.getVisibility
import java.text.SimpleDateFormat
import java.util.*
import java.util.stream.Collectors

class PriceMapActivity : BaseActivity(), PriceMapView, MapObjectTapListener {

    companion object {
        const val PRODUCT_EXTRA = "product"
    }

    @InjectPresenter
    lateinit var presenter: PriceMapPresenter

    private lateinit var locationProviderClient: FusedLocationProviderClient
    private lateinit var product: Product
    private lateinit var pointCollection: MapObjectCollection
    private lateinit var pointColor: PricePointColor

    private var userLocation: LatLng? = null

    private var bottomSheetVisible = false

    override fun initInterface() {
        locationProviderClient = LocationServices.getFusedLocationProviderClient(this)
        product = intent.getSerializableExtra(PRODUCT_EXTRA) as Product
        pointColor = PricePointColor(product.priceMin ?: 0, product.priceMax ?: 0)
        title = product.name

        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        supportActionBar?.setDisplayShowHomeEnabled(true)

        pointCollection = mapView.map.mapObjects.addCollection()
        pointCollection.addTapListener(this)

        getLocationPermission()

        getBottomSheetBehavior().addBottomSheetCallback(object :
            BottomSheetBehavior.BottomSheetCallback() {
            override fun onStateChanged(bottomSheet: View, newState: Int) {
                if (newState == BottomSheetBehavior.STATE_COLLAPSED
                    || newState == BottomSheetBehavior.STATE_HIDDEN
                ) {
                    bottomSheetVisible = false
                }
            }

            override fun onSlide(bottomSheet: View, slideOffset: Float) {
            }
        })
    }

    override fun setAttr() {
        layout = R.layout.activity_price_map
        YandexMapHelper.init(applicationContext, getString(R.string.yandex_map_kit))
    }

    override fun setMapPoints(userPosition: LatLng, points: List<PriceWithShop>) {
        points.forEach { p -> setMapPoint(p) }
    }

    override fun displayShopInfo(priceWithShop: PriceWithShop) {
        if (bottomSheetVisible) {
            setBottomSheetDataWithAnimation(priceWithShop)
        } else {
            setBottomSheetData(priceWithShop)
            displayBottomSheet()
        }
    }

    override fun displayHistory(history: List<Price>) {
        priceChart.setItems(history.stream()
                .map { i -> PriceHistoryItem(
                        i.dateTime ?: Date(), (i.price?.toDouble() ?: 0.0) / 100.0)
                }
                .collect(Collectors.toList()))

        setGraphViewVisibility(true, history.size > 1)
    }

    override fun onRefreshed() {
        refreshUserLocation()
        mapProgressBar.visibility = View.GONE
        mapView.visibility = View.VISIBLE
    }

    override fun onStart() {
        super.onStart()
        YandexMapHelper.onStart(mapView)
    }

    override fun onStop() {
        super.onStop()
        YandexMapHelper.onStop(mapView)
    }

    override fun onMapObjectTap(mapObject: MapObject, point: Point): Boolean {
        val priceWithShop = mapObject.userData as PriceWithShop
        presenter.onShopClicked(priceWithShop)
        return true
    }

    override fun getPermissionRequestCode() = PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION

    override fun onPermissionsGranted() {
        loadMapData()
    }

    private fun setMapPoint(point: PriceWithShop) {
        val foreground = DrawableUtils.tintImage(
            DrawableUtils
                .getBitmapFromVectorDrawable(applicationContext, R.drawable.shop_point),
            pointColor.getColor(applicationContext, point.price ?: 0)
        )

        val background = DrawableUtils.getBitmapFromVectorDrawable(
            applicationContext, R.drawable.shop_point_background
        )

        val bitmap = DrawableUtils.mergeBitmaps(background, foreground)

        val pointOnMap = pointCollection.addPlacemark(
            Point(
                point.shop?.position?.lat ?: return,
                point.shop.position.lng
            ),
            ImageProvider.fromBitmap(bitmap)
        )

        pointOnMap.userData = point
    }

    private fun getLocationPermission() {
        getPermission(Manifest.permission.ACCESS_FINE_LOCATION)
    }

    private fun loadMapData() {
        if (!gettingLocationPermitted()) {
            return
        }

        try {
            val locationResult = LocationServices.getFusedLocationProviderClient(applicationContext)
                    .lastLocation
            locationResult.addOnSuccessListener { location ->
                if (location != null) {
                    userLocation = LatLng(location.latitude, location.longitude)
                }

                loadMapData(userLocation)
            }
        } catch (e: SecurityException) {
        }
    }

    private fun loadMapData(location: LatLng?) {
        if (location != null) {
            presenter.updateMap(product.id!!, null, location.lat, location.lng)
        }
    }

    private fun refreshUserLocation() {
        if (userLocation == null) {
            return
        }

        mapView.map.move(
            CameraPosition(
                Point(userLocation!!.lat, userLocation!!.lng),
                11.0f, 0.0f, 0.0f
            ),
            Animation(Animation.Type.SMOOTH, 0f),
            null
        )

        mapView.map.mapObjects.addPlacemark(
            Point(
                userLocation!!.lat,
                userLocation!!.lng
            ),
            ImageProvider.fromBitmap(
                DrawableUtils
                    .getBitmapFromVectorDrawable(applicationContext, R.drawable.user_location_point)
            )
        )
    }

    private fun setBottomSheetData(priceWithShop: PriceWithShop) {
        setGraphViewVisibility(false)
        priceMapShopName.text = priceWithShop.shop?.name
        priceMapShopAddress.text = priceWithShop.shop?.address
        priceMapShopPrice.text = NumericUtils.createDisplayablePrice(priceWithShop.price)
        priceMapShopPrice.background = DrawableUtils.tintImage(
            applicationContext, R.drawable.price_background,
            pointColor.getColor(applicationContext, priceWithShop.price ?: 0)
        )

        try {
            val dateFormat = SimpleDateFormat("hh:mm | dd.MM.yyyy")
            priceMapShopDate.text = dateFormat.format(
                priceWithShop.dateTime ?: throw RuntimeException()
            )
        } catch (_: java.lang.RuntimeException) {
        }

        presenter.getPriceHistory(
            product.id ?: return,
            priceWithShop.shop?.id ?: return
        )
    }

    private fun setBottomSheetDataWithAnimation(priceWithShop: PriceWithShop) {
        val animator = ValueAnimator.ofFloat(0f, 1f)

        val listener = ValueAnimator.AnimatorUpdateListener { a ->
            setBottomSheetDataAlpha(a.animatedValue as Float)
        }

        animator.addUpdateListener(listener)
        animator.duration = 1000

        setBottomSheetDataAlpha(0f)
        setBottomSheetData(priceWithShop)
        animator.start()
    }

    private fun setBottomSheetDataAlpha(alpha: Float) {
        priceMapShopName.alpha = alpha
        priceMapShopAddress.alpha = alpha
        priceMapShopPrice.alpha = alpha
        priceMapShopDate.alpha = alpha
        priceMapShopPriceDateLabel.alpha = alpha
        priceChartLayout.alpha = alpha
    }

    private fun getBottomSheetBehavior(): BottomSheetBehavior<View> {
        val params = priceMapBottomSheet.layoutParams as CoordinatorLayout.LayoutParams
        return params.behavior as BottomSheetBehavior
    }

    private fun displayBottomSheet() {
        val behavior = getBottomSheetBehavior()
        behavior.state = BottomSheetBehavior.STATE_EXPANDED

        bottomSheetVisible = true
    }

    private fun setGraphViewVisibility(visible: Boolean) =
        setGraphViewVisibility(visible, false)

    private fun setGraphViewVisibility(visible: Boolean, hasHistory: Boolean) {
        priceChart.visibility = getVisibility(visible && hasHistory)
        priceChartEmptyMessage.visibility = getVisibility(visible && !hasHistory)
        priceChartProgressBar.visibility = getVisibility(!visible)
    }
}